<?php

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

class LdapException extends LidasException {

    public function __construct($conn, $previous = NULL) {
        $info = false;
        $message = @ldap_error($conn);
        @ldap_get_option($conn, LDAP_OPT_ERROR_STRING, $info);
        if ($info)
            $message .= ' info: ' . $info;
        parent::__construct($message, @ldap_errno($conn), $previous);
    }

}

class LdapOp {

    protected $conn;
    protected $dn;
    protected $filter;
    protected $scope;

    public function __construct($conn) {
        $this->conn = $conn;
        $this->filter = NULL;
        $this->dn = NULL;
        $this->scope = -1;
    }

    public function from($table) {
        $this->dn = $table;
        return $this;
    }

    public function scope($scope) {
        $this->scope = $scope;
        return $this;
    }

    public function where($filter) {
        $this->filter = $filter;
        return $this;
    }

}

class LdapRecordSet extends LdapOp {

    private $handle;
    private $entry = false;
    private $data = array();

    public function __construct($conn, $handle) {
        parent::__construct($conn);
        $this->handle = $handle;
    }

    public function fetch() {
        $this->entry = ($this->entry === false) ?
                ldap_first_entry($this->conn, $this->handle) :
                ldap_next_entry($this->conn, $this->entry);

        if ($this->entry === false)
            return false;


        $this->data = array();
        $this->data['id'] = ldap_get_dn($this->conn, $this->entry);
        $attrs = ldap_get_attributes($this->conn, $this->entry);
        for ($i = 0; $i < $attrs['count']; $i++) {
            $attr_name = $attrs[$i];
            unset($attrs[$attr_name]['count']);
            $this->data[$attr_name] = $attrs[$attr_name];
        }
        return $this->data;
    }

}

class LdapSelectOp extends LdapOp {

    private $attrs = NULL;

    public function __construct($conn, $fields = NULL) {
        parent::__construct($conn);
        if (is_string($fields))
            $fields = explode(' ', $fields);
        if (is_array($fields))
            $this->attrs = $fields;
    }

    public function exec() {
        if ($this->filter == NULL)
            $this->filter = '(objectClass=*)';

        switch ($this->scope) {
            case 0:
                if (!($r = @ldap_read($this->conn, $this->dn, $this->filter, $this->attrs)))
                    throw new LdapException($this->conn);
                break;
            case 1:
                if (!($r = @ldap_list($this->conn, $this->dn, $this->filter, $this->attrs)))
                    throw new LdapException($this->conn);
                break;
            default:
                if (!($r = @ldap_search($this->conn, $this->dn, $this->filter, $this->attrs)))
                    throw new LdapException($this->conn);
                break;
        }

        return new LdapRecordSet($this->conn, $r);
    }

}

class LdapUpdateOp {

    protected $conn;
    protected $dn;
    protected $filter;
    protected $attrs;
    protected $scope;
    protected $values;
    protected $handle;


    public function __construct($conn, $table) {
        $this->conn = $conn;
        $this->dn = $table;
        $this->filter = '(objectClass=*)';
        $this->scope = 0;
    }

    public function set(Array $values) {
        $this->values = $values;
        return $this;
    }

    public function where($condition) {
        $this->filter = $condition;
        return $this;
    }

    public function exec() {
        if ($this->conn == NULL)
            throw new Exception(__CLASS__ . ": conn could be NULL", 1);
        if ($this->dn == NULL)
            throw new Exception(__CLASS__ . ": dn could be NULL", 1);
        if ($this->values == NULL)
            throw new Exception(__CLASS__ . ": values could be NULL", 1);

        if ($this->filter == NULL)
            $this->filter = '(objectClass=*)';

        $this->attrs = array_keys($this->values);

        switch ($this->scope) {
            case 0:
                if (!($this->handle = @ldap_read($this->conn, $this->dn, $this->filter, $this->attrs)))
                    throw new LdapException($this->conn);
                break;
            case 1:
                if (!($this->handle = @ldap_list($this->conn, $this->dn, $this->filter, $this->attrs)))
                    throw new LdapException($this->conn);
                break;
            default:
                if (!($this->handle = @ldap_search($this->conn, $this->dn, $this->filter, $this->attrs)))
                    throw new LdapException($this->conn);
                break;
        }

        $count = 0;

        if(!($this->entry = @ldap_first_entry($this->conn, $this->handle)))
                    throw new LdapException($this->conn);

        while ($this->entry) {
            $count++;
            $dn = ldap_get_dn($this->conn, $this->entry);
            if(!($r=@ldap_mod_replace($this->conn, $dn, $this->values)))
                    throw new LdapException($this->conn);
            $this->entry = ldap_next_entry($this->conn, $this->entry);
        }
        return $count;
    }
}

class LdapInsertOp extends LdapOp {

}

class LdapDeleteOp extends LdapOp {

}

class Ldap_Backend {

    private $conn = NULL;

    function __destruct() {
        if ($this->conn)
            @ldap_close($this->conn);
    }

    public function open($url, $user, $password) {
        $this->conn = @ldap_connect($url);
        if (!@ldap_set_option($this->conn, LDAP_OPT_PROTOCOL_VERSION, 3))
            throw new LdapException($this->conn);
        if (!@ldap_set_option($this->conn, LDAP_OPT_REFERRALS, 0))
            throw new LdapException($this->conn);
        if (!@ldap_bind($this->conn, $user, $password))
            throw new LdapException($this->conn);
    }

    public function select($fields = NULL) {
        return new LdapSelectOp($this->conn, $fields);
    }

    public function update($table) {
        return new LdapUpdateOp($this->conn, $table);
    }

    public function insert() {
        return new LdapInsertOp($this->conn);
    }

    public function delete() {
        return new LdapDeleteOp($this->conn);
    }

}

?>
